package com.lipaluma.parser;

import java.io.Closeable;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.lipaluma.config.Configuration;
import com.lipaluma.exceptions.CsvParserException;
import com.lipaluma.exceptions.CsvParserValidationException;
import com.lipaluma.field.validator.strategies.ValidationStrategyEnum;
import com.lipaluma.io.SourceReader;
import com.lipaluma.xml.XmlMappings;

/**
 * RowMapper like {@link ColumnDataParser} maps each line of a CSV or Excel file to an Object where the target class is defined by the user.
 * <p>
 * The difference is that RowMapper can map manually line per line. The user manage himself the construction of the list of objects.
 * 
 * @author Mario
 *
 * @param <TARGET> the target entity defined by the user
 */
public abstract class AbstractDataParserIterable<TARGET, CONF extends Configuration> extends AbstractDataParser<TARGET, CONF> implements Closeable {
	private static final Logger LOG = LoggerFactory.getLogger(AbstractDataParserIterable.class);
	private SourceReader sourceReader;	

	public AbstractDataParserIterable(AbstractDataParser.Builder<? extends AbstractDataParserIterable<TARGET, CONF>, TARGET, CONF> builder) {
		super(builder);
	}
	protected AbstractDataParserIterable(Class<TARGET> clazz) {
		super(clazz);
	}
	protected AbstractDataParserIterable(Class<TARGET> clazz, XmlMappings xmlMappings) {
		super(clazz, xmlMappings);
	}

	public boolean hasNext() {
		return sourceReader.hasNext();
	}
	
	/**
	 * Process the next row of the entry file for being mapped on the target entity.
	 * 
	 * @return the Object mapped
	 */
	public TARGET unmarshallNext() {
		if(sourceReader == null)
			throw new CsvParserException("To use unmarshallNext method, you must initialize an input source.");
		try {
			if(sourceReader.hasNext())
				return unmarshal(sourceReader.next());
		} catch (CsvParserValidationException ex) {
			if(validationStrategy.equals(ValidationStrategyEnum.FILTER_VALIDATION_ERRORS)) {
				LOG.warn("Validation Error [object number "+sourceReader.getCurrentLineNumber()+"]"+ex.getMessage());
				LOG.warn("The line will be filtered.");
				return unmarshallNext();
			}
		}
		return null;
	}

	/**
	 * 
	 * 
	 * @return the Object mapped
	 */
	public TARGET unmarshall(String object) {
		SourceReader sr = null;
		try {
			sr = sourceFactory.createSourceReader(new StringReader(object));
			return unmarshal(sr.next());
		} finally {
			if(sr != null)
				sr.close();
		}
	}

	/**
	 * 
	 * 
	 * @return the Object mapped
	 */
	public TARGET unmarshall(Reader reader) {
		SourceReader sr = null;
		try {
			sr = sourceFactory.createSourceReader(reader);
			return unmarshal(sr.next());
		} finally {
			if(sr != null)
				sr.close();
		}
	}

	/**
	 * 
	 * 
	 * @return the Object mapped
	 */
	public TARGET unmarshall(InputStream in) {
		SourceReader sr = null;
		try {
			sr = sourceFactory.createSourceReader(in);
			return unmarshal(sr.next());
		} finally {
			if(sr != null)
				sr.close();
		}
	}

	/**
	 * close the input entry
	 */
	@Override
	public void close() {
		sourceReader.close();
	}
	
	public void setSourceReader(SourceReader sourceReader) {
		this.sourceReader = sourceReader;
	}
}
